/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.karol.lx.fetcher;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.SocketException;
import java.sql.*;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.karol.lx.main.StateManager;
import org.karol.lx.model.GradeRequest;

/**
 * Kelas singleton RequestReseiver. RequestReceiver secara pasif menerima
 * GradeRequest dan mengantrikannya ke RequestDispatcher
 *
 * @author karol
 */
public class RequestFetcher implements Runnable {

    public static final int SOCKET_PORT = 22022;
    private static RequestFetcher sInstance = null;
    private Thread mReceiverThread;
    private ServerSocket mServer;
    private Connection mConnection;
    private DatagramSocket mSocket;
    private RequestReceiver mReceiver = null;

    private RequestFetcher() {
        mReceiverThread = new Thread(this);
        initializeSocket();
    }

    private void initializeSocket() {
        try {
            mSocket = new DatagramSocket(SOCKET_PORT);
        } catch (SocketException ex) {
            Logger.getLogger(RequestFetcher.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void openDBConnection(RequestFetcherConfigurationReader pReader) {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(RequestFetcher.class.getName()).log(Level.SEVERE, null, ex);
        }
        try {
            String tAddress = pReader.getDatabaseAddress();
            String tUser = pReader.getDatabaseUser();
            String tPassword = pReader.getDatabasePassword();

            mConnection = DriverManager.getConnection(tAddress, tUser, tPassword);
        } catch (SQLException ex) {
            Logger.getLogger(RequestFetcher.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public void setReceiver(RequestReceiver pReceiver) {
        mReceiver = pReceiver;
    }

    public static RequestFetcher getInstance() {
        if (sInstance == null) {
            sInstance = new RequestFetcher();
        }
        return sInstance;
    }

    public void main() {
        if (!mReceiverThread.isAlive()) {
            mReceiverThread.start();
        }
    }

    protected synchronized void wakeup() {
        notify();
    }

    private void receivePacket() {
        try {
            byte[] tBuffer = new byte[255];
            DatagramPacket tPacket = new DatagramPacket(tBuffer, tBuffer.length);
            mSocket.receive(tPacket);
        } catch (IOException ex) {
            Logger.getLogger(RequestFetcher.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    protected synchronized void getRequests() {
        while (true) {
            if (true || StateManager.getInstance().getState() == StateManager.STATE_PRIMARY) { ///Only when primary
                Logger.getLogger(RequestFetcher.class.getName()).log(Level.INFO, "Start getting requests...");

                ArrayList<GradeRequest> tPendingRequests = getPendingRequests();
                while (tPendingRequests.size() > 0) { ///Keep getting requests while available
                    for (GradeRequest g : tPendingRequests) {
                        g.take();
                        //WorkDistributor.getInstance().addRequest(g);
                        mReceiver.addRequest(g);
                    }
                    tPendingRequests.clear();
                    //tPendingRequests = getPendingRequests();
                }
            }

            Logger.getLogger(RequestFetcher.class.getName()).log(Level.INFO, "Idle");
            receivePacket();
        }
    }

    /*public void markRequestTaken(GradeRequest pRequest) {
        try {
            Statement tStatement = mConnection.createStatement();
            //Dummy
            tStatement.executeUpdate("UPDATE `grade_requests` SET `status` = 1, `receive_time` = NOW() WHERE `id` = " + pRequest.id + ";");
            //tStatement.executeUpdate("UPDATE `grade_requests` SET `receive_time` = NOW() WHERE `id` = " + pRequest.id + ";");
        } catch (SQLException ex) {
            Logger.getLogger(RequestFetcher.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void markRequestRequeued(GradeRequest pRequest) {
        try {
            pRequest.requeue();
            Statement tStatement = mConnection.createStatement();
            tStatement.executeUpdate("UPDATE `grade_requests` SET `status` = 1 WHERE `id` = " + pRequest.id + ";");
        } catch (SQLException ex) {
            Logger.getLogger(RequestFetcher.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void markRequestDisposed(GradeRequest pRequest) {
        try {
            Statement tStatement = mConnection.createStatement();
            //Dummy
            tStatement.executeUpdate("UPDATE `grade_requests` SET `status` = 3, `receive_time` = NOW() WHERE `id` = " + pRequest.id + ";");
            //tStatement.executeUpdate("UPDATE `grade_requests` SET `receive_time` = NOW() WHERE `id` = " + pRequest.id + ";");
        } catch (SQLException ex) {
            Logger.getLogger(RequestFetcher.class.getName()).log(Level.SEVERE, null, ex);
        }
    }*/

    @Override
    public void run() {
        getRequests();
    }
    
    public Connection getConnection() {
        return mConnection;
    }

    protected ArrayList<GradeRequest> getPendingRequests() {
        try {
            Statement tStatement = mConnection.createStatement();
            //Dummy
            ResultSet set = tStatement.executeQuery("SELECT * FROM `grade_requests` WHERE status = 0 ORDER BY `id` ASC;");
            //ResultSet set = tStatement.executeQuery("SELECT * FROM `grade_requests` WHERE status = 0 ORDER BY `id` DESC LIMIT 1;");
            return GradeRequest.buildFromResultSet(set);
        } catch (SQLException ex) {
            Logger.getLogger(RequestFetcher.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }
}
